package com.teacher.security.config;

import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.teacher.security.entity.WechatUser;
import com.teacher.security.entity.annotation.WhiteApi;
import com.teacher.security.entity.permission.User;
import com.teacher.security.entity.security.AdminSaveDetails;
import com.teacher.security.entity.security.MyUserDetails;
import com.teacher.security.entity.security.RedisUserInfo;
import com.teacher.security.service.IRedisService;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @Author: zfm
 * @Date: 2019/11/11 22:09
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@Slf4j
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private RestfulAccessDeniedHandler restfulAccessDeniedHandler;
    @Autowired
    private RestAuthenticationEntryPoint restAuthenticationEntryPoint;
    @Autowired
    private IRedisService iRedisService;
//
//    @Autowired
//    private IWechatUserService iWechatUserService;

    @Autowired
    private ApplicationContext applicationContext;

    @Override
    public void configure(WebSecurity web) throws Exception {
        List<String> url = new ArrayList<>();
        Map<RequestMappingInfo, HandlerMethod> map = applicationContext.getBean(RequestMappingHandlerMapping.class).getHandlerMethods();
        map.forEach((key, value) -> {
            WhiteApi whiteApi = value.getMethodAnnotation(WhiteApi.class);
            if (whiteApi != null) {
                Set<String> patterns = key.getPatternsCondition().getPatterns();
                System.out.println(JSONUtil.toJsonStr("白名单：" + JSONUtil.toJsonStr(patterns)));
                url.addAll(new ArrayList<>(patterns));
            }
        });
        web.ignoring().antMatchers(url.toArray(new String[0]));
    }

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.csrf()
                .disable()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                // 白名单
                .antMatchers(
                        // swagger api json
                        "/v2/api-docs",
                        // 用来获取支持的动作
                        // 用来获取api-docs的URI
                        "/swagger-resources/**",
                        // 安全选项
                        "/swagger-ui.html",
                        "/webjars/**",
                        "/test/**",
                        "/websocket/**",
                        "/images/**"
                )
                .permitAll()
//                                // 测试时全部运行访问
//                .antMatchers("/**")
//                .permitAll()
                // 跨域请求会先进行一次options请求
                .antMatchers(HttpMethod.OPTIONS)
                .permitAll()
                // 除上面外d所有请求全部需要鉴全认证
                .anyRequest()
                .authenticated()
                .and()
                // 自定义 jwt过滤器
                .addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);

        // 禁用缓存
        httpSecurity.headers().cacheControl();

        //添加自定义未授权和未登录结果返回
        httpSecurity.exceptionHandling()
                .accessDeniedHandler(restfulAccessDeniedHandler)
                .authenticationEntryPoint(restAuthenticationEntryPoint);
    }

    @Override
    @Bean
    public UserDetailsService userDetailsService() {
        //获取登录用户信息
        return openId -> {
//            WechatUser wechatUser = iWechatUserService.getOne(new QueryWrapper<WechatUser>().lambda().eq(WechatUser::getOpenId, openId));
            WechatUser wechatUser = new WechatUser();
            if (wechatUser != null){
                try {
                    RedisUserInfo redisUserInfo = JSONUtil.toBean(iRedisService.get(openId), RedisUserInfo.class);
                    // 从redis拿权限
                    if (redisUserInfo == null) {
                        redisUserInfo = new RedisUserInfo();
                    }
                    List<String> permissionList = redisUserInfo.getPermissionValue();
                    if (permissionList == null) {
                        permissionList = new ArrayList<>();
                    }
                    wechatUser.setOpenId(openId);
                    userSaveDetails().setWechatUser(wechatUser);
                    return new MyUserDetails(wechatUser, permissionList);
                } catch (Exception e) {
                    throw new UsernameNotFoundException(e.getMessage());
                }
            }
            throw new UsernameNotFoundException("没有该用户");
        };
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    public AdminSaveDetails userSaveDetails() {
        return new AdminSaveDetails();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService())
                .passwordEncoder(passwordEncoder());
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter() {
        return new JwtAuthenticationTokenFilter();
    }
}
